package com.gitee.ly.chain.abstractclass;

import com.gitee.ly.chain.Flow;
import com.gitee.ly.chain.Form;
import com.gitee.ly.chain.Link;
import com.gitee.ly.chain.utils.ClassPathResourceUtil;
import com.gitee.ly.chain.utils.StreamEx;

import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * GlobalInfo
 *
 * @author ly
 **/
public class GlobalTool {
    private static boolean loadChain = false;
    private static Map<String, Form> formMap = new HashMap<>();
    private static Map<String, Flow> flowMap = new HashMap<>();
    private static Map<String, List<Link>> flowType2LinkListMap = new HashMap<>();
    private static Map<String, Link> linkMap = new HashMap<>();
    private static Map<String, Map<Form, Integer>> linkType2FormSortMap = new HashMap<>();
    private static Map<String, List<Form>> linkType2FormListMap = new HashMap<>();
    private static Consumer<String> logInfoHandle = null;
    private static Function<Object, String> jsonSerialHandle = null;

    /**
     * 设置日志打印处理
     *
     * @param chain
     */
    public static void setLogInfoHandle(Consumer<String> setLogInfoHandle) {
        logInfoHandle = setLogInfoHandle;
    }

    /**
     * 日志打印
     */
    public static void logInfo(String data) {
        if (logInfoHandle != null) {
            logInfoHandle.accept(data);
        }
    }

    /**
     * 设置json序列化处理
     *
     * @param chain
     */
    public static void setJsonSerialHandle(Function<Object, String> setJsonSerialHandle) {
        jsonSerialHandle = setJsonSerialHandle;
    }

    /**
     * json序列化
     */
    public static String getJsonSerial(Object data) {
        if (jsonSerialHandle != null) {
            return jsonSerialHandle.apply(data);
        } else {
            return data == null ? null : data.toString();
        }
    }

    /**
     * 加载业务流程链
     * <p>
     * 注意：加载的配置文件路径固定在 classpath:chain-load.txt
     */
    public static void loadChain(List<Form> formList) {
        if (loadChain) {
            throw new RuntimeException("classpath:chain-load.txt无法重复加载");
        }
        loadChain = true;
        //校验表单唯一性
        checkFormUniq(formList);
        Map<String, Form> formGroupMap = StreamEx.groupBy(formList, Form::getType);
        formMap = Collections.unmodifiableMap(formGroupMap);
        String chainLoadFilePath = "classpath:chain-load.txt";
        final int[] stage = {0};
        List<Flow> flowList = new ArrayList<>();
        final String[] nowFlowType = {null};
        final List<Link>[] nowFlowTypeLinkList = new List[]{new ArrayList<>()};
        ClassPathResourceUtil.getStreamAndReadEveryLine(chainLoadFilePath, (String line) -> {
            line = line.trim();
            if (line.length() == 0 || line.startsWith("#")) {
                return;
            }
            if (stage[0] == 0 && Objects.equals(line, "[Form]")) {
                stage[0] = 1;
            } else if (stage[0] == 1 && Objects.equals(line, "[Flow]")) {
                stage[0] = 2;
            } else if (stage[0] == 2 && Objects.equals(line, "[Link]")) {
                stage[0] = 3;
                //校验流程唯一性
                checkFlowUniq(flowList);
                Map<String, Flow> flowGroupMap = StreamEx.groupBy(flowList, Flow::getType);
                flowMap = Collections.unmodifiableMap(flowGroupMap);
            } else if (stage[0] == 1) {
                String[] form = line.split(",");
                String formType = form[0];
                String formName = form[1];
                checkFormExist(formList, formType, formName);
            } else if (stage[0] == 2) {
                String[] flow = line.split(",");
                String flowType = flow[0];
                String flowName = flow[1];
                flowList.add(new AbstractTemplateFlow(flowType, flowName));
            } else if ((stage[0] == 3 || stage[0] == 4) && line.startsWith("[")) {
                if (nowFlowType[0] != null && nowFlowTypeLinkList[0] != null && nowFlowTypeLinkList[0].size() > 0) {
                    //校验流程type存在
                    checkFlowExist(nowFlowType[0]);
                    //校验流程下环节及顺序的唯一性
                    nowFlowTypeLinkList[0].sort(Comparator.comparing(Link::getSort));
                    checkFlowLinkListUniq(nowFlowTypeLinkList[0]);
                    flowType2LinkListMap.put(nowFlowType[0], Collections.unmodifiableList(nowFlowTypeLinkList[0]));
                }
                nowFlowType[0] = line.substring(1, line.length() - 1);
                nowFlowTypeLinkList[0] = new ArrayList<>();
                stage[0] = 4;
            } else if (stage[0] == 4) {
                int dotIndex1 = line.indexOf(",");
                int dotIndex2 = line.indexOf(",", dotIndex1 + 1);
                int dotIndex3 = line.indexOf(",", dotIndex2 + 1);
                int linkSort = Integer.parseInt(line.substring(0, dotIndex1));
                String linkType = line.substring(dotIndex1 + 1, dotIndex2);
                String linkName = line.substring(dotIndex2 + 1, dotIndex3);
                AbstractTemplateLink abstractTemplateLink = new AbstractTemplateLink(linkSort, linkType, linkName);
                abstractTemplateLink.setBelongFlowType(nowFlowType[0]);
                //环节表单解析
                Map<Form, Integer> formSortMap = analysisLinkForm(formList, line.substring(dotIndex3 + 1));
                List<Form> linkFormList = new ArrayList<>(formSortMap.keySet());
                //校验环节下表单及顺序的唯一性
                linkFormList.sort(Comparator.comparing(formSortMap::get));
                checkLinkFormListUniq(linkFormList, formSortMap);
                linkType2FormSortMap.put(linkType, formSortMap);
                linkType2FormListMap.put(linkType, Collections.unmodifiableList(linkFormList));
                nowFlowTypeLinkList[0].add(abstractTemplateLink);
            }
        });
        if (nowFlowType[0] != null && nowFlowTypeLinkList[0] != null) {
            //校验流程type存在
            checkFlowExist(nowFlowType[0]);
            //最后一个流程校验流程下环节及顺序的唯一性
            nowFlowTypeLinkList[0].sort(Comparator.comparing(Link::getSort));
            checkFlowLinkListUniq(nowFlowTypeLinkList[0]);
            flowType2LinkListMap.put(nowFlowType[0], Collections.unmodifiableList(nowFlowTypeLinkList[0]));
        }
        flowType2LinkListMap = Collections.unmodifiableMap(flowType2LinkListMap);
        linkType2FormSortMap = Collections.unmodifiableMap(linkType2FormSortMap);
        linkType2FormListMap = Collections.unmodifiableMap(linkType2FormListMap);
        //校验所有环节类型名称的唯一性。且一个环节不可参与多个流程。
        checkAllLinkUniq();
        nowFlowType[0] = null;
        nowFlowTypeLinkList[0] = null;
    }

    /**
     * 环节表单解析
     *
     * @return 环节下参与的表单数据
     */
    private static Map<Form, Integer> analysisLinkForm(List<Form> formList, String linkFormListStr) {
        Map<Form, Integer> formSortMap = new HashMap<>();
        Pattern compile = Pattern.compile("(?<=\\[).+?(?=])");
        Matcher matcher = compile.matcher(linkFormListStr);
        while (matcher.find()) {
            String group = matcher.group();
            String[] formStr = group.split(",");
            int formSort = Integer.parseInt(formStr[0]);
            String formType = formStr[1];
            Form form = formList.stream().filter(a -> Objects.equals(a.getType(), formType)).findFirst().orElse(null);
            if (form == null) {
                throw new RuntimeException(String.format("Link加载时，FormType:%s找不到表单", formType));
            }
            formSortMap.put(form, formSort);
        }

        return formSortMap;
    }

    /**
     * 校验流程type存在
     */
    private static void checkFlowExist(String flowType) {
        if (!flowMap.containsKey(flowType)) {
            throw new RuntimeException(String.format("Link加载时，FlowType:%s无法找到对应流程", flowType));
        }
    }

    /**
     * 校验环节下表单及顺序的唯一性
     *
     * @param formSortMap 环节下表单的顺序
     */
    private static void checkLinkFormListUniq(List<Form> linkFormList, Map<Form, Integer> formSortMap) {
        if (linkFormList == null) {
            return;
        }
        int distinctSortCount = StreamEx.distinctCount(linkFormList, formSortMap::get);
        if (distinctSortCount < linkFormList.size()) {
            throw new RuntimeException("环节下表单存在相同顺序，请检查");
        }
        int distinctTypeCount = StreamEx.distinctCount(linkFormList, Form::getType);
        if (distinctTypeCount < linkFormList.size()) {
            throw new RuntimeException("环节下表单存在相同Type，请检查");
        }
        int distinctNameCount = StreamEx.distinctCount(linkFormList, Form::getName);
        if (distinctNameCount < linkFormList.size()) {
            throw new RuntimeException("环节下表单存在相同Name，请检查");
        }
    }

    /**
     * 校验所有环节类型名称的唯一性。且一个环节不可参与多个流程。
     */
    private static void checkAllLinkUniq() {
        Map<String, Boolean> linkNameMap = new HashMap<>();
        for (String flowType : flowType2LinkListMap.keySet()) {
            List<Link> linkList = flowType2LinkListMap.get(flowType);
            if (linkList != null) {
                for (Link link : linkList) {
                    if (linkMap.containsKey(link.getType())) {
                        throw new RuntimeException(String.format("LinkType:%s 应该全局唯一且不可参与多个流程，存在重复请检查。", link.getType()));
                    }
                    if (linkNameMap.containsKey(link.getName())) {
                        throw new RuntimeException(String.format("LinkName:%s 应该全局唯一且不可参与多个流程，存在重复请检查。", link.getName()));
                    }
                    linkMap.put(link.getType(), link);
                    linkNameMap.put(link.getName(), true);
                }
            }
        }
        linkMap = Collections.unmodifiableMap(linkMap);
    }

    /**
     * 校验流程下环节及顺序的唯一性
     */
    private static void checkFlowLinkListUniq(List<Link> nowFlowTypeLinkList) {
        if (nowFlowTypeLinkList == null) {
            return;
        }
        int distinctSortCount = StreamEx.distinctCount(nowFlowTypeLinkList, Link::getSort);
        if (distinctSortCount < nowFlowTypeLinkList.size()) {
            throw new RuntimeException("流程下环节存在相同顺序，请检查");
        }
        int distinctTypeCount = StreamEx.distinctCount(nowFlowTypeLinkList, Link::getType);
        if (distinctTypeCount < nowFlowTypeLinkList.size()) {
            throw new RuntimeException("流程下环节存在相同Type，请检查");
        }
        int distinctNameCount = StreamEx.distinctCount(nowFlowTypeLinkList, Link::getName);
        if (distinctNameCount < nowFlowTypeLinkList.size()) {
            throw new RuntimeException("流程下环节存在相同Name，请检查");
        }
    }

    private static void checkFormExist(List<Form> formList, String formType, String formName) {
        Form form = StreamEx.findFirstOne(formList, a -> a.getType().equals(formType));
        if (form == null) {
            throw new RuntimeException(String.format("chain-load.txt中配置的Form[%s,%s]找不到java实现类", formType, formName));
        } else if (!Objects.equals(form.getName(), formName)) {
            throw new RuntimeException(String.format("chain-load.txt中配置的Form[%s,%s]和对应java实现类的formName不一致", formType, formName));
        }
    }

    private static void checkFlowUniq(List<Flow> flowList) {
        if (flowList == null) {
            return;
        }
        int typeCount = StreamEx.distinctCount(flowList, Flow::getType);
        if (typeCount < flowList.size()) {
            throw new RuntimeException("Flow存在重复的type。");
        }
        int nameCount = StreamEx.distinctCount(flowList, Flow::getName);
        if (nameCount < flowList.size()) {
            throw new RuntimeException("Flow存在重复的name。");
        }
    }

    private static void checkFormUniq(List<Form> formList) {
        if (formList == null) {
            return;
        }
        int typeCount = StreamEx.distinctCount(formList, Form::getType);
        if (typeCount < formList.size()) {
            throw new RuntimeException("Form存在重复的type。");
        }
        int nameCount = StreamEx.distinctCount(formList, Form::getName);
        if (nameCount < formList.size()) {
            throw new RuntimeException("Form存在重复的name。");
        }
    }

    /**
     * 获取所有流程
     */
    public static List<Flow> getAllFlow() {
        return new ArrayList<>(flowMap.values());
    }

    /**
     * 获取所有表单
     */
    public static List<Form> getAllForm() {
        return new ArrayList<>(formMap.values());
    }

    /**
     * 获取流程
     */
    public static <DataModel> Flow<DataModel> getFlow(String flowType, Class<DataModel> dataModelClass) {
        return flowMap.get(flowType);
    }

    /**
     * 获取环节
     */
    public static Link getLinK(String linkType) {
        return linkMap.get(linkType);
    }

    /**
     * 获取环节下表单的顺序
     */
    public static int getLinkFormSort(String linkType, Form form) {
        Map<Form, Integer> formSortMap = linkType2FormSortMap.get(linkType);
        return formSortMap.get(form);
    }

    /**
     * 获取流程下环节
     */
    public static List<Link> getLinkListByFlowType(String flowType) {
        return flowType2LinkListMap.get(flowType);
    }

    /**
     * 获取环节下表单
     */
    public static List<Form> getFormListByLinkType(String linkType) {
        return linkType2FormListMap.get(linkType);
    }

}
